/*
 * Copyright 2010 Electronic Business Systems Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package gr.ebs.gssdroid.http;

import gr.ebs.gssdroid.async.FileMonitor;
import gr.ebs.gssdroid.async.GssAsyncTask;
import gr.ebs.gssdroid.util.UserDetails;

import java.io.File;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;

import android.util.Log;

public class GssHttpClient {
	UserDetails userDetails;
	private static final SimpleDateFormat DATE_FORMATER = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US);
    static {
        DATE_FORMATER.setTimeZone(TimeZone.getTimeZone("GMT"));
    }
    
	public GssHttpClient(UserDetails userDetails) {
		super();
		this.userDetails = userDetails;
	}
	
	
	 protected void buildHeaders(HttpRequestBase request,String path){
	    	String date = getDateString();
	    	String resource = path.substring(userDetails.getApiPath().length()-1,path.length());
	    	int index = resource.indexOf("?");
	    	String res = (index==-1)? resource : resource.substring(0, index);
			String sig = sign(request.getMethod(), date, res,userDetails.getToken());
			request.setHeader("X-GSS-Date", date);
			//request.setHeader("X-GSS-Authorization", userDetails.getUsername() + " " + sig);
			request.setHeader("Authorization", userDetails.getUsername() + " " + sig);
			Log.d("SIG:", userDetails.getUsername() + " " + sig);
			request.setHeader("Accept", "application/json; charset=utf-8");
	    }
	    
	    private String getDateString(){
	    	return DATE_FORMATER.format(new Date());
	    }
	    
	    private String sign(String httpMethod, String timestamp, String path,
				String token) {
			String input = httpMethod + timestamp + path;
			String signed = null;
			try {
				// Get an HMAC-SHA1 key from the authentication token.
				byte[] unencodedToken = Base64.decodeBase64(token.getBytes());
				SecretKeySpec signingKey = new SecretKeySpec(unencodedToken,
						"HmacSHA1");
				// Get an HMAC-SHA1 Mac instance and initialize with the signing
				// key.
				Mac hmac = Mac.getInstance("HmacSHA1");
				hmac.init(signingKey);
				// Compute the HMAC on the input data bytes.
				byte[] rawMac = hmac.doFinal(input.getBytes());
				// Do base 64 encoding
				signed = new String(Base64.encodeBase64(rawMac), "US-ASCII");

			} catch (InvalidKeyException ikex) {
				System.err.println("Fatal key exception: " + ikex.getMessage());
				ikex.printStackTrace();
			} catch (UnsupportedEncodingException ueex) {
				System.err
						.println("Fatal encoding exception: " + ueex.getMessage());
			} catch (NoSuchAlgorithmException nsaex) {
				System.err.println("Fatal algorithm exception: "
						+ nsaex.getMessage());
				nsaex.printStackTrace();
			}

			return signed;
		}
	    
	    public HttpResponse executeGet(String path) throws SystemErrorException{
	        HttpClient client = new DefaultHttpClient();
	        HttpGet request = new HttpGet(path);
	        buildHeaders(request, path);
	        HttpResponse response;
			try {
				response = client.execute(request);
			} catch (Exception e) {
				throw new SystemErrorException("Unable to execute http call", e);
			}
	        return response;
	    }
	    
	    public HttpResponse executeHead(String path) throws SystemErrorException{
	        HttpClient client = new DefaultHttpClient();
	        HttpGet request = new HttpGet(path);
	        buildHeaders(request, path);
	        HttpResponse response;
			try {
				response = client.execute(request);
			} catch (Exception e) {
				throw new SystemErrorException("Unable to execute http call", e);
			}
	        return response;
	    }
	    
	    public HttpResponse executePut(String path,String uri, GssAsyncTask task, int number) throws SystemErrorException{
	        HttpClient client = new DefaultHttpClient();
	        HttpPut request = new HttpPut(path);
	        Log.i("XX","path is:"+uri);
	        buildHeaders(request, path);
	        GssFileEntity bin = new GssFileEntity(new File(uri),"binary/octet-stream", new FileMonitor(new File(uri).length(), task), number);
	        
	        request.setEntity(bin);
	        HttpResponse response;
			try {
				response = client.execute(request);
			} catch (Exception e) {
				throw new SystemErrorException("Unable to execute http call", e);
			}
	        return response;
	    }
	    
	    public HttpResponse executePut(String path,String uri) throws SystemErrorException{
	        HttpClient client = new DefaultHttpClient();
	        HttpPut request = new HttpPut(path);
	        Log.i("XX","path is:"+uri);
	        buildHeaders(request, path);
	        FileEntity bin = new FileEntity(new File(uri),"binary/octet-stream");
	        request.setEntity(bin);
	        HttpResponse response;
			try {
				response = client.execute(request);
			} catch (Exception e) {
				throw new SystemErrorException("Unable to execute http call", e);
			}
	        return response;
	    }
	    
	    public HttpResponse executePut(String path,InputStream input, long inputLength) throws SystemErrorException{
	        HttpClient client = new DefaultHttpClient();
	        HttpPut request = new HttpPut(path);
	        buildHeaders(request, path);
	        request.setEntity(new InputStreamEntity(input, inputLength));
	        HttpResponse response;
			try {
				response = client.execute(request);
			} catch (Exception e) {
				throw new SystemErrorException("Unable to execute http call", e);
			}
	        return response;
	    }
	    
	    
	    
	    protected HttpResponse executePost(String path, String data) throws SystemErrorException{
	        HttpClient client = new DefaultHttpClient();
	        HttpPost request = new HttpPost(path);
	        buildHeaders(request, path);
	        if(data!=null){
	        	
				try {
					StringEntity ent = new StringEntity(data,"UTF-8");
					request.setEntity(ent);
				} catch (UnsupportedEncodingException e) {
					throw new SystemErrorException("Unable to execute http call", e);
				}
	        	
	        }
	        HttpResponse response;
			try {
				response = client.execute(request);
			} catch (Exception e) {
				throw new SystemErrorException("Unable to execute http call", e);
			}
	        return response;
	    }
	    
	    public HttpResponse executeDelete(String path) throws SystemErrorException{
	        HttpClient client = new DefaultHttpClient();
	        HttpDelete request = new HttpDelete(path);
	        buildHeaders(request, path);
	        HttpResponse response;
			try {
				response = client.execute(request);
			} catch (Exception e) {
				throw new SystemErrorException("Unable to execute http call", e);
			}
	        return response;
	    }
	
}
